#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>

#include <pthread.h>
/* prctl Only support in Linux */
#include <sys/prctl.h>
#include <time.h>
/* 枚举：线程运行标志 */
typedef enum __thread_flag{
    THREAD_FLAG_STOP    = 0,
    THREAD_FLAG_RUNNING = 1,
    THREAD_FLAG_NONE    = -1
}THREAD_FLAG;

typedef struct __thread_handle
{
    THREAD_FLAG     tFlag;
    pthread_t       tID;
    void*           tPara;
    void*           (*tFunction)(void* para);
    char            tName[64];
    time_t 			uptime;
    char			runtime[256];
}THR_HND;
/* 记录所有创建的线程实例 */
int nthrhnd = 0;
struct __thread_handle* thrhnd_array[256];

void THREAD_SetProperty_before_infinite_loop(struct __thread_handle* thrhnd, char* thread_name)
{
  /* 设置线程名称 */
  strcpy(thrhnd->tName, thread_name);
  prctl(PR_SET_NAME, thrhnd->tName);
  //fprintf(stderr, "Thread/> set thread name '%s' successfully.\n", thrhnd->tName);
  /* 允许其他线程在线取消当前线程 */
  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
  /*异步取消当前线程， 本线程接到其他线程的取消信号后，立即退出*/
  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
}

int THREAD_Construct(struct __thread_handle* thrhnd, 
                      void* (*function)(void* para), void* parameter)
{
  thrhnd->tFunction = function;
  thrhnd->tPara = parameter;
  if(pthread_create(&thrhnd->tID, NULL, thrhnd->tFunction, thrhnd->tPara) != 0){
    thrhnd->tFlag = THREAD_FLAG_NONE;
        perror("Thread/> {ERROR} pthread_create");
        return -1;
    }else{
      thrhnd->tFlag = THREAD_FLAG_RUNNING;
      thrhnd_array[nthrhnd] = thrhnd;
      nthrhnd++;
      //记录创建线程时间点
      time(&(thrhnd->uptime));
      //printf("Thread/> {INFO} Create thread successfully.\n"); 
      return 0;
    }
}

void THREAD_Deconstruct(struct __thread_handle* thrhnd)
{
  void *ret=NULL; 
  /* 取消线程 */
  pthread_cancel(thrhnd->tID); 
  /* 阻塞等待线程结束并释放资源完成. */
  pthread_join(thrhnd->tID, &ret);  
  thrhnd->tFlag = THREAD_FLAG_NONE;
  thrhnd->tPara = NULL;
  thrhnd->tFunction = NULL;
  fprintf(stderr, "Thread/> {INFO} %s closed.\n", thrhnd->tName);
}

struct __thread_handle* thr_get_handle(pthread_t thr_id)
{
  int i;
  for(i = 0; i < nthrhnd; i++){
    if(thrhnd_array[i]->tID == thr_id){
      return thrhnd_array[i];
    }
  }
  return NULL;
}

#define thr_property(thr, name)		THREAD_SetProperty_before_infinite_loop(thr, name)
#define thr_construct(thr, fx, arg)	THREAD_Construct(thr, fx, arg)
#define thr_release(thr)			THREAD_Deconstruct(thr)
//====================================================================================

#ifndef CONNECT_SIZE
#define CONNECT_SIZE 	256
#endif

#define PORT 			7777
#define MAX_LINE 		2048
#define LISTENQ 		20
#define MAX_tcp_CLT		32

typedef struct __client_connect{
	int 		clt_idx;
	socklen_t 	clt_len;
	int 		clt_connfd;
	struct sockaddr_in clt_addr;

}CLTCONN;

typedef struct __tcp_conn{
	CLTCONN   conn[MAX_tcp_CLT];
	int       idx;
}TCPCLT;

void setNonblocking(int sockfd)
{
	int opts;
    opts=fcntl(sockfd,F_GETFL);
    if(opts<0)
    {
        perror("fcntl(sock,GETFL)");
        return;
    }

    opts = opts|O_NONBLOCK;
    if(fcntl(sockfd,F_SETFL,opts)<0)
    {
 		perror("fcntl(sock,SETFL,opts)");
        return;
    }
}

int client_index(TCPCLT tcp, int epoll_ev_fd)
{
	int i;
	for(i = 0; i < tcp.idx; i++)
	{
		if(tcp.conn[i].clt_connfd == epoll_ev_fd)
			return tcp.conn[i].clt_idx;
	}
	return -1;
}


#define INTCMD_NONE		0
#define INTCMD_EXIT		1
int internal_command_run(char* str)
{
	if(strncmp("`EXIT", str, strlen(str)-1) == 0)
	{
		return INTCMD_EXIT;
	}
	return INTCMD_NONE;
}

void* thrfunc_tcpconn(void* tcp_port)
{
	int* port =(int*)tcp_port;
	char thrname[16]; sprintf(thrname, "tcpthr_%d", *port);
	thr_property(thr_get_handle(pthread_self()), thrname);

	int i, listenfd, sockfd, epfd, nfds;

	ssize_t n, ret;
		
	char buf[MAX_LINE];

	struct sockaddr_in servaddr;

	/* 初始化tcp客户端连接池 */
	struct __tcp_conn  tcp; tcp.idx = 0;
	for(i = 0; i < MAX_tcp_CLT; i++)
	{
		tcp.conn[i].clt_len = sizeof(tcp.conn[i].clt_addr);
	}

	/*声明epoll_event变量，ev用于注册事件，数组用于回传要处理的事件*/
	struct epoll_event ev, events[20];

	/*(1) 得到监听描述符
		SOCK_STREAM		TCP 传输层
		SOCK_DGRAM      UDP 传输层
		SOCK_RAW 		ICMP报文, IGMP报文  网络层
	*/
	listenfd = socket(AF_INET , SOCK_STREAM , 0);
	setNonblocking(listenfd);

	/*生成用于处理accept的epoll专用文件描述符*/	
	epfd = epoll_create(CONNECT_SIZE);
	/*设置监听描述符*/
	ev.data.fd = listenfd;
	/*设置处理事件类型*/
	ev.events = EPOLLIN | EPOLLET;
	/*注册事件*/
	epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);		

	/*(2) 绑定套接字*/	
	bzero(&servaddr , sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(*port);

	bind(listenfd , (struct sockaddr *)&servaddr , sizeof(servaddr));

	/*(3) 监听*/
	listen(listenfd , LISTENQ);

	/*(4) 进入服务器接收请求循环*/
	while(1)
	{
		/*等待事件发生*/
		nfds = epoll_wait(epfd , events , CONNECT_SIZE , -1);
		if(nfds <= 0)
			continue;
	
		/*处理发生的事件*/
		for(i=0 ; i<nfds ; ++i)
		{
			/*检测到新客户端链接*/
			if(events[i].data.fd == listenfd)
			{	
				/*接收客户端的请求*/
				//tcp.conn[tcp.idx].clt_connfd
				//tcp.conn[tcp.idx].clt_addr
				//tcp.conn[tcp.idx].clt_len
				//tcp.conn[tcp.idx].clt_idx
				if((tcp.conn[tcp.idx].clt_connfd = 
					accept(listenfd , (struct sockaddr *)&(tcp.conn[tcp.idx].clt_addr) , &(tcp.conn[tcp.idx].clt_len))) < 0)
				{
					perror("accept error.\n");
					exit(1);
				}	

				printf("accpet a new client: %s:%d\n", inet_ntoa(tcp.conn[tcp.idx].clt_addr.sin_addr) , 
																tcp.conn[tcp.idx].clt_addr.sin_port);
			
				/*设置为非阻塞*/
				setNonblocking(tcp.conn[tcp.idx].clt_connfd);
				ev.data.fd = tcp.conn[tcp.idx].clt_connfd;
				ev.events = EPOLLIN | EPOLLET;
				epoll_ctl(epfd , EPOLL_CTL_ADD , tcp.conn[tcp.idx].clt_connfd , &ev);
				tcp.conn[tcp.idx].clt_idx = ++tcp.idx;	//客户端数量+1
			}
			/*如果是已链接客户端，并且收到数据，则读取数据*/
			else if(events[i].events & EPOLLIN){

				if((sockfd = events[i].data.fd) < 0)
					continue;
				bzero(buf , MAX_LINE);
				//printf("reading the socket~~~\n");
				if((n = read(sockfd , buf , MAX_LINE)) <= 0)
				{
					close(sockfd);
					events[i].data.fd = -1;
				}
				else{

					if(internal_command_run(buf) == INTCMD_EXIT){
						goto EXITSVC;
					}
					buf[n] = '\0';
					printf("clint[%d] send message: %s", client_index(tcp, events[i].data.fd), buf);
				
					/*设置用于注册写操作文件描述符和事件*/
					ev.data.fd = sockfd;
					ev.events = EPOLLOUT| EPOLLET;	
					epoll_ctl(epfd , EPOLL_CTL_MOD , sockfd , &ev);			
				}										
			}
			else if(events[i].events & EPOLLOUT)
			{
				if((sockfd = events[i].data.fd) < 0)
				continue;
				if((ret = write(sockfd , buf , n)) != n)	
				{
					printf("error writing to the sockfd!\n");
					break;
				}
				/*设置用于读的文件描述符和事件*/
				ev.data.fd = sockfd;
				ev.events = EPOLLIN | EPOLLET;
				/*修改*/
				epoll_ctl(epfd , EPOLL_CTL_MOD , sockfd , &ev);				
			}
		}

	}
EXITSVC:
	close(listenfd);
	close(epfd);
	exit(0);
}

void* thrfunc_udpconn(void* udp_port)
{
	int* port =(int*)udp_port;
	char thrname[16]; sprintf(thrname, "udpthr_%d", *port);
	thr_property(thr_get_handle(pthread_self()), thrname);

	int i, listenfd, sockfd, epfd, nfds;

	ssize_t n, ret;
		
	char buf[MAX_LINE];

	struct sockaddr_in servaddr;

	/* 初始化tcp客户端连接池 */
	struct __tcp_conn  tcp; tcp.idx = 0;
	for(i = 0; i < MAX_tcp_CLT; i++)
	{
		tcp.conn[i].clt_len = sizeof(tcp.conn[i].clt_addr);
	}

	/*声明epoll_event变量，ev用于注册事件，数组用于回传要处理的事件*/
	struct epoll_event ev, events[20];

	/*(1) 得到监听描述符
		SOCK_STREAM		TCP 传输层
		SOCK_DGRAM      UDP 传输层
		SOCK_RAW 		ICMP报文, IGMP报文  网络层
	*/
	listenfd = socket(AF_INET , SOCK_DGRAM , 0);
	setNonblocking(listenfd);

	/*生成用于处理accept的epoll专用文件描述符*/	
	epfd = epoll_create(CONNECT_SIZE);
	/*设置监听描述符*/
	ev.data.fd = listenfd;
	/*设置处理事件类型*/
	ev.events = EPOLLIN | EPOLLET;
	/*注册事件*/
	epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);		

	/*(2) 绑定套接字*/	
	bzero(&servaddr , sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(*port);

	bind(listenfd , (struct sockaddr *)&servaddr , sizeof(servaddr));

	/*(3) UDP 没有监听*/
	//listen(listenfd , LISTENQ);

	/*(4) 进入服务器接收请求循环*/
	while(1)
	{
		/*等待事件发生*/
		nfds = epoll_wait(epfd , events , CONNECT_SIZE , -1);
		if(nfds <= 0)
			continue;
	
		/*处理发生的事件*/
		for(i=0 ; i<nfds ; ++i)
		{
			/*检测到新客户端链接*/
			if(events[i].data.fd == listenfd)
			{	
				/*接收客户端的请求*/
				//tcp.conn[tcp.idx].clt_connfd
				//tcp.conn[tcp.idx].clt_addr
				//tcp.conn[tcp.idx].clt_len
				//tcp.conn[tcp.idx].clt_idx
				if((tcp.conn[tcp.idx].clt_connfd = 
					accept(listenfd , (struct sockaddr *)&(tcp.conn[tcp.idx].clt_addr) , &(tcp.conn[tcp.idx].clt_len))) < 0)
				{
					perror("accept error.\n");
					exit(1);
				}	

				printf("accpet a new client: %s:%d\n", inet_ntoa(tcp.conn[tcp.idx].clt_addr.sin_addr) , 
																tcp.conn[tcp.idx].clt_addr.sin_port);
			
				/*设置为非阻塞*/
				setNonblocking(tcp.conn[tcp.idx].clt_connfd);
				ev.data.fd = tcp.conn[tcp.idx].clt_connfd;
				ev.events = EPOLLIN | EPOLLET;
				epoll_ctl(epfd , EPOLL_CTL_ADD , tcp.conn[tcp.idx].clt_connfd , &ev);
				tcp.conn[tcp.idx].clt_idx = ++tcp.idx;	//客户端数量+1
			}
			/*如果是已链接客户端，并且收到数据，则读取数据*/
			else if(events[i].events & EPOLLIN){

				if((sockfd = events[i].data.fd) < 0)
					continue;
				bzero(buf , MAX_LINE);
				//printf("reading the socket~~~\n");
				if((n = read(sockfd , buf , MAX_LINE)) <= 0)
				{
					close(sockfd);
					events[i].data.fd = -1;
				}
				else{

					if(internal_command_run(buf) == INTCMD_EXIT){
						goto EXITSVC;
					}
					buf[n] = '\0';
					printf("clint[%d] send message: %s", client_index(tcp, events[i].data.fd), buf);
				
					/*设置用于注册写操作文件描述符和事件*/
					ev.data.fd = sockfd;
					ev.events = EPOLLOUT| EPOLLET;	
					epoll_ctl(epfd , EPOLL_CTL_MOD , sockfd , &ev);			
				}										
			}
			else if(events[i].events & EPOLLOUT)
			{
				if((sockfd = events[i].data.fd) < 0)
				continue;
				if((ret = write(sockfd , buf , n)) != n)	
				{
					printf("error writing to the sockfd!\n");
					break;
				}
				/*设置用于读的文件描述符和事件*/
				ev.data.fd = sockfd;
				ev.events = EPOLLIN | EPOLLET;
				/*修改*/
				epoll_ctl(epfd , EPOLL_CTL_MOD , sockfd , &ev);				
			}
		}

	}
EXITSVC:
	close(listenfd);
	close(epfd);
	exit(0);
}

/*
 编译: $ gcc -o litesvc litesvc.c -lpthread
 */
typedef struct tcpthr{
	THR_HND hnd;
	int 	port;
}TCPTHR;

typedef struct udpthr{
	THR_HND hnd;
	int 	port;
}UDPTHR;

int main(int argc, char* argv[])
{
	TCPTHR tcpthr[10];
	TCPTHR udpthr[10];
	int i;
	for(i = 0; i < argc-1; i++)
	{
		tcpthr[i].port = atoi(argv[i+1]);
		udpthr[i].port = atoi(argv[i+1]);
		thr_construct(&tcpthr[i].hnd, thrfunc_tcpconn, (void*)&tcpthr[i].port);
		thr_construct(&udpthr[i].hnd, thrfunc_udpconn, (void*)&udpthr[i].port);
	}

	while(1){


	}



	return 0;
}